﻿using Microsoft.Extensions.DependencyInjection;
using Polly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace suirui.ZhuMu.Middlewares
{
    /// <summary>
    /// 来自
    /// https://blog.csdn.net/qq_34202873/article/details/106475138
    /// </summary>
    public static class PollyHttpClientServiceCollectionExtensions
    {
        /// <summary>
        /// Httpclient扩展方法
        /// </summary>
        /// <param name="services">ioc容器</param>
        /// <param name="name">HttpClient 名称(针对不同的服务进行熔断，降级)</param>
        /// <param name="action">熔断降级配置</param>
        /// <param name="TResult">降级处理错误的结果</param>
        /// <returns></returns>
        public static IServiceCollection AddHttpClientPolly(this IServiceCollection services, string name, Action<HttpClientPollyOptions> action)
        {
            // 1、创建选项配置类
            HttpClientPollyOptions options = new HttpClientPollyOptions();
            action(options);
            // 2、配置httpClient,熔断降级策略
            services.AddHttpClient(name)
            //1.1 降级策略
            .AddPolicyHandler(Policy<HttpResponseMessage>.HandleInner<Exception>().FallbackAsync(options.httpResponseMessage, async b =>
            {
                // 1、降级打印异常
                Console.WriteLine($"服务{name}开始降级,异常消息：{b.Exception.Message}");
                // 2、降级后的数据
                Console.WriteLine($"服务{name}降级内容响应：{options.httpResponseMessage.Content.ToString()}");
                await Task.CompletedTask;
            }))
            // 1.2 断路器策略
            .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().CircuitBreakerAsync(options.CircuitBreakerOpenFallCount, TimeSpan.FromSeconds(options.CircuitBreakerDownTime), (ex, ts) => {
                Console.WriteLine($"服务{name}断路器开启，异常消息：{ex.Exception.Message}");
                Console.WriteLine($"服务{name}断路器开启时间：{ts.TotalSeconds}s");
            }, () => {
                Console.WriteLine($"服务{name}断路器关闭");
            }, () => {
                Console.WriteLine($"服务{name}断路器半开启(时间控制，自动开关)");
            }))
            // 1.3 重试策略
            .AddPolicyHandler(Policy<HttpResponseMessage>
              .Handle<Exception>()
              .RetryAsync(options.RetryCount)
            )
            // 1.4 超时策略
            .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(options.TimeoutTime)));
            return services;
        }	
    }

    public class HttpClientPollyOptions
    {
        /// <summary>
        /// 超时时间设置，单位为秒
        /// </summary>
        public int TimeoutTime { set; get; }
        /// <summary>
        /// 失败重试次数
        /// </summary>
        public int RetryCount { set; get; }

        /// <summary>
        /// 执行多少次异常，开启短路器（例：失败2次，开启断路器）
        /// </summary>
        public int CircuitBreakerOpenFallCount { set; get; }

        /// <summary>
        /// 断路器开启的时间(例如：设置为2秒，短路器两秒后自动由开启到关闭)
        /// </summary>
        public int CircuitBreakerDownTime { set; get; }

        /// <summary>
        /// 降级处理(将异常消息封装成为正常消息返回，然后进行响应处理，例如：系统正在繁忙，请稍后处理.....)
        /// </summary>
        public HttpResponseMessage httpResponseMessage { set; get; }	
    }
}
